﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Shapes;
using System.Windows.Controls;
using System.Windows.Media;
using WickedFlame.Core.History;
using WickedFlame.Core.Shapes;
using WickedFlame.Core.Tools;
using WickedFlame.Core.Layers;
using System.Collections.ObjectModel;
using System.ComponentModel;
//using System.Runtime.Serialization;

namespace WickedFlame.Core
{
    /// <summary>
    /// Delegate that will be executed when a layer is added
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    public delegate void LayerAddedEventHandler(object sender, LayerAddedEventArgs e);
	
    /// <summary>
    /// DrawingCanvas control.
    /// This canvas can be added to the WPF form for drawing on.
    /// By default contains 1 layer for drawing shapes on.
    /// Layers with shapes can be added dynamically.
    /// </summary>
    public class DrawingCanvas : CanvasBase, INotifyPropertyChanged
    {
        enum ToolLayerType { TopLayer, BottomLayer };

		#region Events

		/// <summary>
        /// Event gets executed when a layer is added
        /// </summary>
        public event LayerAddedEventHandler LayerAdded;

		/// <summary>
		/// Event that gets called when the dirty bit changes
		/// </summary>
		public event EventHandler DirtyChanged;

		/// <summary>
		/// General event that gest called when the canvas changed
		/// </summary>
		public event EventHandler CanvasChanged;

		/// <summary>
		/// Event that gets called when the selection of the shapes changes
		/// </summary>
        public static event EventHandler SelectionChanged;

		#endregion

		#region Construction / Destruction

		/// <summary>
        /// Creates a new canvas for drawing on.
        /// By default contais 1 layer
        /// </summary>
        public DrawingCanvas()
        {
            Initialize();
        }

        bool _initialized = false;
        public void Initialize()
        {
            if (!_initialized)
            {
                InitializeComponent();
                InitializeHelperLayers();

                _historyManager = new HistoryManager(this);
            }

            //set this instance to the private static so that static vars can use it
            InitializedDrawingCanvas = this;
            _initialized = true;
        }

        /// <summary>
        /// deletes all layers and the shapes contained in the layer
        /// has to be executed everytime when the canvas will be destroyed
        /// </summary>
        public void Dispose()
        {
        }

        private void InitializeComponent()
        {
            this.Loaded += new RoutedEventHandler(OnLoaded);
            this.SizeChanged += new SizeChangedEventHandler(Canvas_SizeChanged);
        }

		private void InitializeHelperLayers()
        {
            ToolLayers = new Dictionary<ToolLayerType, Layer>();

            var bottomLayer = new BottomLayer();
            bottomLayer.IsSelecteable = true;
            bottomLayer.IsGridVisible = ShowGrid;
            ToolLayers.Add(ToolLayerType.BottomLayer, bottomLayer);

            this.Children.Add(bottomLayer);

            var topLayer = new TopLayer();
            topLayer.IsSelecteable = true;
            ToolLayers.Add(ToolLayerType.TopLayer, topLayer);

            this.Children.Add(topLayer);
        }

		#endregion

		#region INotifyPropertyChanged Members

		public event PropertyChangedEventHandler PropertyChanged;

        protected void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        #endregion

        #region Base Events

        private void OnLoaded(object sender, RoutedEventArgs e)
        {
            this.Background = TransparentBackgroudBrush;
            //this.Background = Brushes.White;
        }

        private void Canvas_SizeChanged(object sender, System.Windows.SizeChangedEventArgs e)
        {
            if (Layers != null)
                foreach (CanvasLayer layer in Layers)
                {
                    layer.Width = e.NewSize.Width;
                    layer.Height = e.NewSize.Height;
                }

            if (ToolLayers[ToolLayerType.TopLayer] != null)
            {
                ToolLayers[ToolLayerType.TopLayer].Width = e.NewSize.Width;
                ToolLayers[ToolLayerType.TopLayer].Height = e.NewSize.Height;
            }

            if (ToolLayers[ToolLayerType.BottomLayer] != null)
            {
                ToolLayers[ToolLayerType.BottomLayer].Width = e.NewSize.Width;
                ToolLayers[ToolLayerType.BottomLayer].Height = e.NewSize.Height;
            }
        }
        
        #endregion

        #region Layers

        Dictionary<ToolLayerType, Layer> ToolLayers;
        public BottomLayer BottomLayer
        {
            get
            {
                if (ToolLayers.ContainsKey(ToolLayerType.BottomLayer))
                    return ToolLayers[ToolLayerType.BottomLayer] as BottomLayer;
                return null;
            }
        }

        public TopLayer TopLayer
        {
            get
            {
                if (ToolLayers.ContainsKey(ToolLayerType.TopLayer))
                    return ToolLayers[ToolLayerType.TopLayer] as TopLayer;
                return null;
            }
        }


        CanvasLayer _selectedLayer;
        /// <summary>
        /// CanvasLayer that is selected for drawing on
        /// </summary>
        public CanvasLayer SelectedLayer
        {
            get
            {
                return _selectedLayer;
            }
            set
            {
                if (_canvasLayers.Contains(value) || value == null)
                {
                    _selectedLayer = value;
                    if (_selectedLayer != null)
                        _selectedLayer.IsSelecteable = true;

                    DrawTrackers();
                }
                else
                    throw new CanvasException("Layer is not contained in layer collection.\nPlease add lyer first!");

                NotifyPropertyChanged("SelectedLayer");
                NotifyPropertyChanged("SelectedLayerIndex");
            }
        }

        public int SelectedLayerIndex
        {
            get
            {
                if (Layers == null)
                    return -1;

                for (int i = 0; i < Layers.Count; i++)
                {
                    if (Layers[i] == SelectedLayer)
                        return i;
                }
                return -1;
            }
            set
            {
                if (Layers == null | Layers.Count - 1 < value || value < 0)
                    return;

                SelectedLayer = Layers[value];
            }
        }

        ObservableCollection<CanvasLayer> _canvasLayers;
        /// <summary>
        /// List of layers in this canvas
        /// </summary>
        public ObservableCollection<CanvasLayer> Layers
        {
            get
            {
                return _canvasLayers;
            }
            private set
            {
                _canvasLayers = value;
                if (_canvasLayers.Count > 0)
                {
                    SelectedLayer.IsSelecteable = false;
                    SelectedLayer = _canvasLayers[_canvasLayers.Count - 1];
                    SelectedLayer.IsSelecteable = true;
                }
            }
        }

        /// <summary>
        /// adds a empty layer to the canvas
        /// </summary>
        public void AddLayer()
        {
			var layer = new CanvasLayer();

			layer.ShapeAdded += new ShapeChangedEventHandler(LayerChanged);
			layer.ShapeDeleted += new ShapeChangedEventHandler(LayerChanged);
			layer.EndShapeChange += new ShapeChangedEventHandler(LayerChanged);

            AddLayer(layer);
            if (Layers.Count > 1)
                MarkDirty();
        }

        /// <summary>
        /// adds a layer to the canvas
        /// </summary>
        /// <param name="layer">layer to be added</param>
        public void AddLayer(CanvasLayer layer)
        {
            if (_canvasLayers == null)
                _canvasLayers = new ObservableCollection<CanvasLayer>();

            //_canvasLayers.Add(layer);
            _canvasLayers.Insert(0, layer); ;

            //topLayer has to be the top layer so insert 1 underneath
            this.Children.Insert(this.Children.Count - 1, layer);

            layer.Height = this.Height;
            layer.Width = this.Width;

            if (SelectedLayer != null)
            {
                SelectedLayer.IsSelecteable = false;

                DrawTrackers();
            }

            SelectedLayer = layer;

            if (LayerAdded != null)
                LayerAdded(this, new LayerAddedEventArgs(layer));

            //don't add the first layer to the change history!
            //first layer is not allowed to undo!
            if (Layers.Count > 1)
                PushHistoryCommand(new LayerAddedCommand(SelectedLayer, this, SelectedLayerIndex));

            //some tools need cleaning up
			if (ToolsControler.SelectedTool != null)
				ToolsControler.SelectedTool.OnDeactivate(this);

			layer.ShapeAdded += new ShapeChangedEventHandler(LayerChanged);
			layer.ShapeDeleted += new ShapeChangedEventHandler(LayerChanged);
			layer.EndShapeChange += new ShapeChangedEventHandler(LayerChanged);
        }

        /// <summary>
        /// removes the layer that is currently selected
        /// </summary>
        public void RemoveLayer()
        {
            if (SelectedLayer != null)
            {
				var layer = SelectedLayer;

                PushHistoryCommand(new LayerDeleteCommand(SelectedLayer, this, SelectedLayerIndex));
                //SelectedLayer.Dispose();
				_canvasLayers.Remove(layer);
				this.Children.Remove(layer);

				layer.ShapeAdded -= new ShapeChangedEventHandler(LayerChanged);
				layer.ShapeDeleted -= new ShapeChangedEventHandler(LayerChanged);
				layer.EndShapeChange -= new ShapeChangedEventHandler(LayerChanged);
            }

            if (_canvasLayers.Count > 0)
                SelectedLayer = _canvasLayers[0];
            else
                SelectedLayer = null;

            this.InvalidateVisual();

            DrawTrackers();
        }

        /// <summary>
        /// removes the given layer from the canvas
        /// </summary>
        /// <param name="layer"></param>
        public void RemoveLayer(CanvasLayer layer)
        {
            if (_canvasLayers.Contains(layer))
            {
                int li = 0;
                li = _canvasLayers.IndexOf(layer);
                //for (int i = 0; i < _canvasLayers.Count; i++)
                //    if (_canvasLayers[i] == layer)
                //        li = i;
                PushHistoryCommand(new LayerDeleteCommand(layer, this, li));

                _canvasLayers.Remove(layer);
                this.Children.Remove(layer);

				layer.ShapeAdded -= new ShapeChangedEventHandler(LayerChanged);
				layer.ShapeDeleted -= new ShapeChangedEventHandler(LayerChanged);
				layer.EndShapeChange -= new ShapeChangedEventHandler(LayerChanged);
            }

            if (_canvasLayers.Count > 0)
                SelectedLayer = _canvasLayers[0];
            else
                SelectedLayer = null;

            DrawTrackers();
        }

        public void ClearLayers()
        {
            foreach (CanvasLayer l in Layers)
            {
				l.ShapeAdded -= new ShapeChangedEventHandler(LayerChanged);
				l.ShapeDeleted -= new ShapeChangedEventHandler(LayerChanged);
				l.EndShapeChange -= new ShapeChangedEventHandler(LayerChanged);
                this.Children.Remove(l);
            }
            Layers.Clear();
            DrawTrackers();

            NotifyPropertyChanged("SelectedLayerIndex");
        }

		void LayerChanged(object sender, ShapeChangedEventArgs e)
		{
			if (CanvasChanged != null)
				CanvasChanged(this, e);
		}

        public void MoveLayerUp(CanvasLayer layer)
        {
            int i = Layers.IndexOf(layer);

            //if (i < Layers.Count - 1)
            //{
            //    Layers.Remove(layer);
            //    Layers.Insert(i + 1, layer);

            //    i = this.Children.IndexOf(layer);
            //    this.Children.Remove(layer);
            //    this.Children.Insert(i + 1, layer);
            //}

            if (i > 0)
            {
                Layers.Remove(layer);
                Layers.Insert(i - 1, layer);

                i = this.Children.IndexOf(layer);
                this.Children.Remove(layer);
                this.Children.Insert(i + 1, layer);
            }

            NotifyPropertyChanged("SelectedLayerIndex");
			if (CanvasChanged != null)
				CanvasChanged(this, new EventArgs());
        }

        public void MoveLayerDown(CanvasLayer layer)
        {
            int i = Layers.IndexOf(layer);

            //if (i > 0)
            //{
            //    Layers.Remove(layer);
            //    Layers.Insert(i-1, layer);

            //    i = this.Children.IndexOf(layer);
            //    this.Children.Remove(layer);
            //    this.Children.Insert(i-1, layer);
            //}

            if (i < Layers.Count - 1)
            {
                Layers.Remove(layer);
                Layers.Insert(i + 1, layer);

                i = this.Children.IndexOf(layer);
                this.Children.Remove(layer);
                this.Children.Insert(i - 1, layer);
            }

            NotifyPropertyChanged("SelectedLayerIndex");
			if (CanvasChanged != null)
				CanvasChanged(this, new EventArgs());
        }

        /// <summary>
        /// Refreshes all layers and resets the clip
        /// </summary>
        public void Refresh()
        {
            foreach (CanvasLayer layer in Layers)
            {
                layer.Refresh();
            }
            Initialize();
            
            DrawTrackers();
        }

        #endregion

        #region History

        HistoryManager _historyManager;

        public HistoryManager HistoryManager
        {
            get
            {
                return _historyManager;
            }
        }

        /// <summary>
        /// Add command to history.
        /// </summary>
        public void PushHistoryCommand(HistoryCommand command)
        {
            _historyManager.PushHistoryCommand(command);
        }

        /// <summary>
        /// Clear command history.
        /// </summary>
        public void ClearHistory()
        {
            _historyManager.ClearHistory();
            DrawTrackers();
        }

        /// <summary>
        /// Undo
        /// </summary>
        public void Undo()
        {
            _historyManager.Undo();
            DrawTrackers();
        }

        /// <summary>
        /// Redo
        /// </summary>
        public void Redo()
        {
            _historyManager.Redo();
            DrawTrackers();
        }

        #endregion

        #region Shapes

        /// <summary>
        /// Adds a VectorShape to the canvas/layer and registers it in the shapelist of the layer
        /// </summary>
		/// <param name="shape"></param>
        public void AddShape(VectorShape shape)
        {
            SelectedLayer.AddShape(shape);
            this.InvalidateVisual();

            MarkDirty();
        }

        /// <summary>
        /// Adds a VectorShape to the canvas/layer, registers it in the shapelist of the layer and adds a history command
        /// </summary>
        /// <param name="shape"></param>
        /// <param name="addhistory"></param>
        public void AddShape(VectorShape shape, bool addhistory)
        {
            AddShape(shape);
            if(addhistory)
                PushHistoryCommand(new WickedFlame.Core.History.AddCommand(shape, SelectedLayerIndex));

            MarkDirty();
        }

        public void InsertShape(int index, VectorShape shape)
        {
            SelectedLayer.ShapeList.Insert(index, shape);

            MarkDirty();
        }

        public void RemoveShape(VectorShape shape)
        {
            SelectedLayer.RemoveShape(shape);
            MarkDirty();
        }

        public void RemoveSelection()
        {
            SelectedLayer.RemoveSelection();
            MarkDirty();

            DrawTrackers();
        }

        /// <summary>
        /// Selects all objects in the curent layer that intersect with the rectangle
        /// </summary>
        /// <param name="rectangle"></param>
        public void SelectShapes(Rect rectangle)
        {
            SelectedLayer.SelectShapes(rectangle);
        }

        #endregion

        #region Properties

        DrawingBrush tbBrush;
        private DrawingBrush TransparentBackgroudBrush
        {
            get
            {
                if (tbBrush == null)
                {
                    tbBrush = new DrawingBrush();

                    var backgroundSquare =
                        new GeometryDrawing(
                            Brushes.White,
                            null,
                            new RectangleGeometry(new Rect(0, 0, 10, 10)));

                    var aGeometryGroup = new GeometryGroup();
                    aGeometryGroup.Children.Add(new RectangleGeometry(new Rect(0, 0, 5, 5)));
                    aGeometryGroup.Children.Add(new RectangleGeometry(new Rect(5, 5, 5, 5)));

                    var checkerBrush = new SolidColorBrush(Colors.LightGray);

                    var checkers = new GeometryDrawing(checkerBrush, null, aGeometryGroup);

                    var checkersDrawingGroup = new DrawingGroup();
                    checkersDrawingGroup.Children.Add(backgroundSquare);
                    checkersDrawingGroup.Children.Add(checkers);

                    tbBrush.Drawing = checkersDrawingGroup;
                    double vpw = (100 / this.Width) * 0.1;
                    double vph = (100 / this.Height) * 0.1;

                    tbBrush.Viewport = new Rect(0, 0, vpw, vph);
                    tbBrush.TileMode = TileMode.Tile;
                }
                return tbBrush;
            }
        }

        int _zoom = 100;
        /// <summary>
        /// Gets or sets the persantage of he zoom
        /// </summary>
        public int Zoom
        {
            get
            {
                return _zoom;
            }
            set
            {
                _zoom = value;
            }
        }

        #endregion

        #region Static/Global scope (Tools, Options, Helper Methods)

        static DrawingCanvas InitializedDrawingCanvas;

        #region Options

        static bool _showGrid = false;
        public static bool ShowGrid
        {
            get
            {
                return _showGrid;
            }
            set
            {
                _showGrid = value;
                if (InitializedDrawingCanvas != null)
                    InitializedDrawingCanvas.BottomLayer.IsGridVisible = value;
            }
        }

        #endregion

        #region Tools
		
		static ToolControler _toolsControler;
		public static ToolControler ToolsControler
		{
			get
			{
				if (_toolsControler == null)
					_toolsControler = new ToolControler();
				return _toolsControler;
			}
		}

		static VectorTool _oldTool;
		internal static void InitializeNewTool(VectorTool tool)
		{
			if (InitializedDrawingCanvas != null)
				if (_oldTool != null)
					_oldTool.OnDeactivate(InitializedDrawingCanvas);

			if (InitializedDrawingCanvas != null)
				_oldTool.OnActivate(InitializedDrawingCanvas);
			_oldTool = tool;
		}


        #endregion

        #region Metadata

        public void MarkDirty()
        {
            if (!IsDirty)
            {
                IsDirty = true;
            }
        }

        bool _isDirty = false;
        public bool IsDirty
        {
            get
            {
				return _isDirty;
            }
            set
            {
				_isDirty = value;
                NotifyPropertyChanged("IsDirty");
				if (DirtyChanged != null)
					DirtyChanged(this, new EventArgs());
            }
        }

        #endregion

        #region Helper Methods

        /// <summary>
        /// Draws the trackers of the selected shapes.
        /// The trackers are drawn in the top layer (helper layer) so that the trackers will not be covered by other shapes.
        /// </summary>
        public static void DrawTrackers()
        {
            if (InitializedDrawingCanvas != null)
            {
                InitializedDrawingCanvas.TopLayer.DrawTrackers(InitializedDrawingCanvas);
                if (SelectionChanged != null)
                    SelectionChanged(InitializedDrawingCanvas, new EventArgs());
            }
        }

        #endregion

        #endregion
    }

    public class LayerAddedEventArgs : EventArgs
    {
        CanvasLayer _layer;

        public LayerAddedEventArgs(CanvasLayer layer)
        {
            _layer = layer;
        }

        public CanvasLayer Layer
        {
            get
            {
                return _layer;
            }
        }
    }
}
